package com.study.common.utils;

/**
 * @Description: 坐标转换,将火星(中国GPS)坐标转为地球(世界)坐标
 * @author: 林循益
 * @date: 2017/9/12 19:35
 * @Company: magicyowelink.
 * @version: V1.0
 */
public class CoordinateUtil {
    private static double a = 6378245.0;
    private static double ee = 0.00669342162296594323;


    private static Rectangle[] exclude;
    private static Rectangle[] region;

    public static class Rectangle {
        private double west;
        private double north;
        private double east;
        private double south;

        Rectangle(double latitude1, double longitude1, double latitude2, double longitude2) {
            this.west = Math.min(longitude1, longitude2);
            this.east = Math.max(longitude1, longitude2);
            this.north = Math.max(latitude1, latitude2);
            this.south = Math.min(latitude1, latitude2);
        }
    }


    static {
        region = new Rectangle[]{
                new Rectangle(49.220400, 79.446200, 42.889900, 96.330000),
                new Rectangle(54.141500, 109.687200, 39.374200, 135.000200),
                new Rectangle(42.889900, 73.124600, 29.529700, 124.143255),
                new Rectangle(29.529700, 82.968400, 26.718600, 97.035200),
                new Rectangle(29.529700, 97.025300, 20.414096, 124.367395),
                new Rectangle(20.414096, 107.975793, 17.871542, 111.744104),
        };

        exclude = new Rectangle[] {
                new Rectangle(25.398623, 119.921265, 21.785006, 122.497559),
                new Rectangle(22.284000, 101.865200, 20.098800, 106.665000),
                new Rectangle(21.542200, 106.452500, 20.487800, 108.051000),
                new Rectangle(55.817500, 109.032300, 50.325700, 119.127000),
                new Rectangle(55.817500, 127.456800, 49.557400, 137.022700),
                new Rectangle(44.892200, 131.266200, 42.569200, 137.022700),
        };
    }

    /**
     * 手机GPS坐标转火星坐标
     *
     * @param
     * @return
     */
    public static double[] transformFromWGSToGCJ(double latitude, double longitude) {

        //如果在国外，则默认不进行转换
        if(!isInsideChina(latitude, longitude)) {
            return new double[]{latitude, longitude};
        }

        double dLat = transformLat(longitude - 105.0,
                latitude - 35.0);
        double dLon = transformLon(longitude - 105.0,
                latitude - 35.0);
        double radLat = latitude / 180.0 * Math.PI;
        double magic = Math.sin(radLat);
        magic = 1 - ee * magic * magic;
        double sqrtMagic = Math.sqrt(magic);
        dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * Math.PI);
        dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * Math.PI);

        return new double[]{latitude + dLat, longitude + dLon};
    }

    public static double transformLat(double x, double y) {
        double ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y
                + 0.2 * Math.sqrt(x > 0 ? x : -x);
        ret += (20.0 * Math.sin(6.0 * x * Math.PI) + 20.0 * Math.sin(2.0 * x
                * Math.PI)) * 2.0 / 3.0;
        ret += (20.0 * Math.sin(y * Math.PI) + 40.0 * Math.sin(y / 3.0
                * Math.PI)) * 2.0 / 3.0;
        ret += (160.0 * Math.sin(y / 12.0 * Math.PI) + 320 * Math.sin(y
                * Math.PI / 30.0)) * 2.0 / 3.0;
        return ret;
    }

    public static double transformLon(double x, double y) {
        double ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1
                * Math.sqrt(x > 0 ? x : -x);
        ret += (20.0 * Math.sin(6.0 * x * Math.PI) + 20.0 * Math.sin(2.0 * x
                * Math.PI)) * 2.0 / 3.0;
        ret += (20.0 * Math.sin(x * Math.PI) + 40.0 * Math.sin(x / 3.0
                * Math.PI)) * 2.0 / 3.0;
        ret += (150.0 * Math.sin(x / 12.0 * Math.PI) + 300.0 * Math.sin(x
                / 30.0 * Math.PI)) * 2.0 / 3.0;
        return ret;
    }

    public static boolean outOfChina(double lat, double lon) {
        if (lon < 72.004 || lon > 137.8347)
            return true;
        return lat < 0.8293 || lat > 55.8271;
    }


    private static Boolean isInsideChina(double latitude, double longitude) {
        for (Rectangle aRegion : region) {
            if (inRectangle(aRegion, longitude, latitude)) {
                for (Rectangle anExclude : exclude) {
                    if (inRectangle(anExclude, longitude, latitude)) {
                        return false;
                    }
                }
                return true;
            }
        }
        return false;
    }

    private static Boolean inRectangle(Rectangle rect, double longitude, double latitude) {
        return longitude >= rect.west  && longitude <= rect.east && latitude <= rect.north && latitude >= rect.south;
    }


    public static double distance_on_geoid(double lat1, double lon1, double lat2, double lon2) {

        if(lat1 == lat2 && lon1 == lon2) {
            return 0.0;
        }


        // Convert degrees to radians
        lat1 = lat1 * Math.PI / 180.0;
        lon1 = lon1 * Math.PI / 180.0;

        lat2 = lat2 * Math.PI / 180.0;
        lon2 = lon2 * Math.PI / 180.0;

        // radius of earth in metres
        double r = 6378100;

        // P
        double rho1 = r * Math.cos(lat1);
        double z1 = r * Math.sin(lat1);
        double x1 = rho1 * Math.cos(lon1);
        double y1 = rho1 * Math.sin(lon1);

        // Q
        double rho2 = r * Math.cos(lat2);
        double z2 = r * Math.sin(lat2);
        double x2 = rho2 * Math.cos(lon2);
        double y2 = rho2 * Math.sin(lon2);

        // Dot product
        double dot = (x1 * x2 + y1 * y2 + z1 * z2);
        double cos_theta = dot / (r * r);

        double theta = Math.acos(cos_theta);


        // Distance in Metres
        return r * theta;
    }

}
